﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Serna
{
    internal class ObjectDefinition
    {        
        public enum eObjectDescription
        {
            Unknown,           
            /// <summary>
            /// User's class
            /// </summary>
            CustomClass,
            Dictionary,
            HashSet,
            List,
            Array
        }

        [Flags]
        public enum eDescriptor : long
        {
            EMPTY = 0,
            /// <summary>
            /// Flag descriptor, tells that this object is serialized without key
            /// Array like element
            /// </summary>
            SERIALIZED_WITHOUT_KEY = 1
        }
       
        /// <summary>
        /// 
        /// </summary>
        public eObjectDescription Description = eObjectDescription.Unknown;

        /// <summary>
        /// 
        /// </summary>
        public eDescriptor Descriptor = eDescriptor.EMPTY;

        /// <summary>
        /// Extra attributes for the object. Default is null
        /// </summary>
        public string Attribute = null;

        /// <summary>
        /// name of the object
        /// </summary>
        public string ObjectName { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public Type ObjectType = null;

        /// <summary>
        /// Delegate for creation of the instance
        /// </summary>
        public Func<object> InstanceCreator = null;

        ///// <summary>
        ///// Holds field name as a key, value is FieldDefinition
        ///// For getter/setter function, first param always instance of the object
        ///// </summary>
        //public Dictionary<string, FieldDefinition> Fields = new Dictionary<string, FieldDefinition>();

        /// <summary>
        /// Holds field name as a key, value is PropertyDefinition
        /// For getter/setter function, first param always instance of the object
        /// </summary>
        public Dictionary<string, PropertyDefinition> Properties = new Dictionary<string, PropertyDefinition>();

        /// <summary>
        /// Concerns properties and fields
        /// Returns null if not found
        /// </summary>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public PropertyDefinition GetPropertyDefinitionByName(string propertyName)
        {
            PropertyDefinition pd = null;
            if (Properties.TryGetValue(propertyName, out pd))
                return pd;

            return null;
        }
                

        //Hybrid Dicitonary
        //http://stackoverflow.com/questions/11388607/get-key-and-value-types-of-non-generic-idictionary-at-runtime

        /// <summary>
        /// In case of IDictionary we hold here type of Dictionary Key, for HashSet or IList - type of Value        
        /// </summary>
        public Type GenericArgument1 = null;
        /// <summary>
        /// In case of IDictionary, we hold here type for Dictionary Value
        /// </summary>
        public Type GenericArgument2 = null;

        /// <summary>
        /// Secially designed for DictionaryType.
        /// This action as a first argument gets instance of the Generic Dictionary, then Key and Value. After that tries to insert KVP into Dictionary
        /// </summary>
        public Action<object, object, object> Dictionary_AddElement = null;
        /// <summary>
        /// Secially designed for ListType.
        /// </summary>
        public Action<object, object> List_AddElement = null;
        /// <summary>
        /// Secially designed for HashSet Type.
        /// </summary>
        public Func<object, object, bool> HashSet_AddElement = null;
        /// <summary>
        /// Interesting for Dictionary, hashset, List
        /// If key or object are Complex, correspondent converter will stay null
        /// </summary>
        public Func<string, Tuple<bool, object>> keyTypeFromStringConverter = null;
        /// <summary>
        /// Interesting for Dictionary, hashset, List
        /// If key or object are Complex, correspondent converter will stay null
        /// </summary>
        public Func<string, Tuple<bool, object>> valueTypeFromStringConverter = null;

        public Action<StringBuilder, object> keyTypeToStringConverter = null;
        public Action<StringBuilder, object> valueTypeToStringConverter = null;

        //public int ArrayRank = 0;
        //public Action<object, int, object> GetValueSetterForArray1D = null;
        //public Action<object, int, int, object> GetValueSetterForArray2D = null;
        //public Action<object, int, int, int, object> GetValueSetterForArray3D = null;
        //public Func<Type, int, object> GetInstanceCreatorForArrays1D = null;
        //public Func<Type, int, int, object> GetInstanceCreatorForArrays2D = null;
        //public Func<Type, int, int, int, object> GetInstanceCreatorForArrays3D = null;
    }


    /// <summary>
    /// Holds tools for Properties and Fields
    /// </summary>
    internal class PropertyDefinition
    {       
        public enum eProperty
        {
            Field,
            Property,
            Array
        }

        /// <summary>
        /// For getter/setter function, first param always instance of the object
        /// </summary>
        public Func<object, object> Getter { get; set; }
        /// <summary>
        /// Instance and value of this property
        /// </summary>
        public Action<object, object> Setter { get; set; }
        public Type Type { get; set; }
        public eProperty Mode = eProperty.Property;
        //public System.ComponentModel.TypeConverter Converter = null;
        public Func<string, Tuple<bool, object>> TypeConverterFromString = null;
        public Action<StringBuilder, object> TypeConverterToString = null;
        

    }



}
